package cc.aiee.wxpay.core.tool;

import java.io.FileInputStream;
import java.io.InputStream;
import java.security.KeyStore;
import java.util.HashMap;
import java.util.Map;

import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.servlet.http.HttpServletRequest;

import cc.aiee.core.kit.KitHttp;
import cc.aiee.core0.bean.J;
import cc.aiee.core0.bean.R;
import cc.aiee.core0.enums.MsgCodeEnum;
import cc.aiee.core0.enums.StatusEnum;
import cc.aiee.wxpay.core.constants.WxPayConst;
import cc.aiee.wxpay.core.enums.WxPaySignType;
import cc.aiee.wxpay.core.pojo.dto.WxPayNotifyDTO;
import cc.aiee.wxpay.core.pojo.dto.WxPayXmlDTO;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.XmlUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.digest.DigestAlgorithm;
import cn.hutool.crypto.digest.HmacAlgorithm;

public class WxPayTool {
	@Deprecated
	public static String getSign(Map<String, Object> params, String apiSecret) {
//		String sortJoin = MapUtil.sortJoin(params, "&", "=", true, "&key=" + apiSecret);
		String signParams = SecureUtil.signParams(DigestAlgorithm.MD5, params, "&", "=", true, ("&key=" + apiSecret));
//		String mapToLinkString = KitSignature.mapToLinkString(params);
//		mapToLinkString = mapToLinkString+"&key="+apiSecret;
//		SecureUtil.signParamsMd5(params, otherParams);

		return signParams.toUpperCase();
	}

	public static String signCreate(Map<String, Object> params, String apiSecret, WxPaySignType signType) {
		// 生成签名前先去除sign
		params.remove(WxPayConst.SIGN);

		String sortJoin = MapUtil.sortJoin(params, "&", "=", true, ("&key=" + apiSecret));

		if (signType == null) {
			signType = WxPaySignType.MD5;
		}
		String sign = null;
		switch (signType) {
		case HMAC_SHA256:
			sign = SecureUtil.hmac(HmacAlgorithm.HmacSHA256, apiSecret).digestHex(sortJoin);
			break;
		default:
			sign = SecureUtil.md5(sortJoin);
			break;
		}
		return sign.toUpperCase();
	}

	@Deprecated
	public static String getSingSHA256(Map<String, Object> params, String apiSecret) {
		String signParams = SecureUtil.signParams(DigestAlgorithm.SHA256, params, "&", "=", true,
				("&key=" + apiSecret));
		return signParams.toUpperCase();
	}

	@Deprecated
	public static String getHmacSHA256(Map<String, Object> params, String apiSecret) {
		String sortJoin = MapUtil.sortJoin(params, "&", "=", true, ("&key=" + apiSecret));
		String digestHex = SecureUtil.hmac(HmacAlgorithm.HmacSHA256, apiSecret).digestHex(sortJoin).toUpperCase();
		return digestHex;
	}

	@Deprecated
	public static void signSHA256(Map<String, Object> params, String apiSecret) {
		// 签名
		params.put(WxPayConst.SIGN, getSingSHA256(params, apiSecret));
	}
	@Deprecated
	public static void signHmacSHA256(Map<String, Object> params, String apiSecret) {
		// 签名
		params.put(WxPayConst.SIGN, getHmacSHA256(params, apiSecret));
	}
	
	/**
	 * @deprecated use signCreate
	 * @param params
	 * @param apiSecret
	 */
	public static void sign(Map<String, Object> params, String apiSecret) {
		// 签名
		params.put(WxPayConst.SIGN, getSign(params, apiSecret));
	}

	public static J<String, WxPayNotifyDTO> getNotifyDTO(HttpServletRequest request) {
		String readData = KitHttp.readData(request);
		Map<String, Object> xmlToMap = XmlUtil.xmlToMap(readData);
		WxPayNotifyDTO mapToBean = BeanUtil.mapToBean(xmlToMap, WxPayNotifyDTO.class, true);
		if (false == signVerify(xmlToMap)) {
			mapToBean.setResult_code("FAIL");
			mapToBean.setErr_code_des("Sign Verify Fail");
			return new J<>(MsgCodeEnum.F_SIGN_6, mapToBean, readData);
		}
		return new J<>(ok(mapToBean), mapToBean, readData);
	}

	public static R<WxPayNotifyDTO> getNotifyDTO(String xml) {
		Map<String, Object> xmlToMap = XmlUtil.xmlToMap(xml);
		WxPayNotifyDTO mapToBean = BeanUtil.mapToBean(xmlToMap, WxPayNotifyDTO.class, true);
		if (false == signVerify(xmlToMap)) {
			mapToBean.setResult_code("FAIL");
			mapToBean.setErr_code_des("Sign Verify Fail");
			return new R<>(MsgCodeEnum.F_SIGN_6, mapToBean);
		}
		return new R<>(ok(mapToBean), mapToBean);
	}

	@Deprecated
	public static boolean signVerify(Map<String, Object> xmlToMap) {
		return true;
	}

	public static boolean signVerify(Map<String, Object> xmlToMap, String apiSecret, WxPaySignType signType) {
		String sign = String.valueOf(xmlToMap.get(WxPayConst.SIGN));
		String createSign = signCreate(xmlToMap, apiSecret, signType);
		return createSign.equals(sign);
	}

	public static <T> T xmlToBean(String xmlStr, Class<T> beanClass) {
		Map<String, Object> xmlToMap = XmlUtil.xmlToMap(xmlStr);
		return BeanUtil.mapToBean(xmlToMap, beanClass, true);
	}

	public static MsgCodeEnum ok(WxPayXmlDTO data) {
		MsgCodeEnum result = MsgCodeEnum.EMPTY;
		if (false == StrUtil.equals("SUCCESS", data.getReturn_code())) {
			result.setMsg(data.getReturn_msg());
			result.setStatus(StatusEnum.FAIL);
			result.setCode("-1");
			return result;
		}
		if (false == StrUtil.equals("SUCCESS", data.getResult_code())) {
			result.setMsg(data.getErr_code_des());
			result.setStatus(StatusEnum.FAIL);
			result.setCode(data.getErr_code());
			return result;
		}
		return MsgCodeEnum.SUCCESS;
	}

	public static KeyManager[] getKeyManager(String certPass, String certPath, InputStream certFile) throws Exception {
		KeyStore clientStore = KeyStore.getInstance("PKCS12");
		if (certFile != null) {
			clientStore.load(certFile, certPass.toCharArray());
		} else {
			clientStore.load(new FileInputStream(certPath), certPass.toCharArray());
		}
		KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
		kmf.init(clientStore, certPass.toCharArray());
		return kmf.getKeyManagers();
	}

	/**
	 * 返回回调信息
	 * 
	 * <pre>
		 &lt;xml&gt;
		  &lt;return_code&gt;&lt;![CDATA[SUCCESS]]&gt;&lt;/return_code&gt;
		  &lt;return_msg&gt;&lt;![CDATA[OK]]&gt;&lt;/return_msg&gt;
		 &lt;/xml&gt;
	 * </pre>
	 * 
	 * @param code SUCCESS/FAIL
	 * @param msg
	 * @return
	 */
	public static String returnNotifyMsg(String code, String msg) {
		Map<String, String> map = new HashMap<>();
		map.put("return_code", code);
		map.put("return_msg", msg);
		return XmlUtil.mapToXmlStr(map);
	}

	public static String returnNotifyMsg() {
		return returnNotifyMsg("SUCCESS", "OK");
	}
}
